package util.estatisticas;

import util.Ponto;
import util.Util;
import util.graficos.Grafico;

public abstract class AnalisadorFaseTransiente {
	
	protected double areaAcumulada;
	
	protected double t0;
	
	protected double n0;
	
	// vai sendo atualizado ao longo da fase transiente.
	private Ponto pontoEquilibrio;
	
	private boolean equilibrio;
	
	private Grafico grafico;
	
	private Grafico graficoMedio;
	
	/**
	 * Determinamos o final da fase transiente utilizando uma maquina de estados
	 * A variavel estado determina em qual estado da maquina estamos.
	 * Sempre que a inclinacao atual for menor do que o valor determinado na configuracao da simulacao,
	 * avancamos um estado na maquina. Se não for, voltamos ao estado inicial.
	 */
	private int estado;
	
	/**
	 * Construtor
	 * 
	 * @param areaAcumulada
	 * @param t0
	 * @param n0
	 */
	public AnalisadorFaseTransiente(double areaAcumulada, double t0, double n0) {
		this.areaAcumulada = areaAcumulada;
		this.t0 = t0;
		this.n0 = n0;
		this.grafico = null;
		this.graficoMedio = null;
		
		this.equilibrio = false;
		this.pontoEquilibrio = new Ponto(-1, -1);
		this.estado = 0;
	}

	/**
	 * Usando a idéia de uma máquina de estados, é responsável pela determinação do fim da fase
	 * transiente.
	 * Utiliza-se dos parâmetros definidos pelo usuário para isso.
	 * 
	 * @param xAtual
	 * @param yAtual
	 */
	private void calculaInclinacao(final double xAtual, final double yAtual) {
		if (isEquilibrio())
			return;
		
		if (this.pontoEquilibrio.getX() < 0 || this.pontoEquilibrio.getY() < 0) {
			this.pontoEquilibrio.setX(xAtual);
			this.pontoEquilibrio.setY(yAtual);
		}
		else {
			double diffX = xAtual - this.pontoEquilibrio.getX();
			double diffY = yAtual - this.pontoEquilibrio.getY();
			
			// diffX == diffY == 0 pode acontecer quando a população for constante.
			if (diffX > 0) {
				double inclinacao = Math.abs(diffY / diffX);
			
				this.pontoEquilibrio.setX(xAtual);
				this.pontoEquilibrio.setY(yAtual);
				
				if (inclinacao <= Util.INCLINACAO_MINIMA) {
					this.estado++;
					if (this.estado >= Util.TOTAL_VERIFICACOES) {
						this.equilibrio = true;
						// Final da fase transiente
					}
				} else {
					this.estado = 0;
				}
			}
		}
	}
	
	protected void calculaAreaAcumulada(final double t1) {
		this.areaAcumulada += this.n0 * (t1 - this.t0);
		
		if (t1 > 0) {				
			double nMedio = this.areaAcumulada / t1;
			
			if (Util.PLOTAR_GRAFICOS) {
				this.graficoMedio.adicionaPonto(t1, nMedio);
			}
			
			this.calculaInclinacao(t1, nMedio);
		}
	}
	

	public void plotPontoEquilibrio() {
		if (Util.PLOTAR_GRAFICOS) {
			if (isEquilibrio()) {
				this.graficoMedio.setPontoFinalFaseTransiente(this.pontoEquilibrio);
			}
		}
	}
	
	/**
	 * 
	 * @return Sistema está em equilíbrio, isto é, se atingiu o final da fase transiente.
	 */
	public boolean isEquilibrio() {
		return this.equilibrio;
	}

	protected Grafico getGrafico() {
		return grafico;
	}

	protected void setGrafico(Grafico grafico) {
		this.grafico = grafico;
	}

	protected Grafico getGraficoMedio() {
		return graficoMedio;
	}

	protected void setGraficoMedio(Grafico graficoMedio) {
		this.graficoMedio = graficoMedio;
	}
}
